package com.boil.qz.safekavass.aspect;

import com.boil.pojo.ResultVo;
import com.boil.util.Constants;
import com.boil.util.Utils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;

/**
 * Created by ChenYong on 2017-08-28.
 * <br>
 * 请求参数切面组件。
 * <br>
 * <span style="color: red;">注意：自动在 Controller 的方法执行之前执行参数校验，非常厉害吧！！！</span>
 */
@Component
@Aspect
@Order(Constants.VALUE_1)
public class RequestParamAspect {
    /**
     * 日志
     */
    private static Logger log = LoggerFactory.getLogger(RequestParamAspect.class);

    /**
     * 定义切入点。
     */
    @Pointcut("execution(* com.boil.qz.safekavass.web.controller.*.*(..))")
    public void pointcut() {
    }

    /**
     * 执行顺序：1。
     * <br>
     * 当目标方法开始执行时，可以执行拦截操作，阻止目标方法执行。
     *
     * @param proceedingJoinPoint 连接点实例
     * @return 实例
     * @throws Throwable 抛出异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 获取目标方法的参数
        Object[] args = proceedingJoinPoint.getArgs();

        // 判断参数里是否包含了校验参数
        for (Object o : args) {
            if (o instanceof BindingResult) {
                Utils.hasRequestParamError(new ResultVo(), (BindingResult) o);
            }
        }

        return proceedingJoinPoint.proceed();
    }

    /**
     * 执行顺序：2。
     * <br>
     * 当目标方法开始执行时，执行 before 方法。
     *
     * @param joinPoint 连接点实例
     */
    @Before(value = "pointcut()")
    public void before(JoinPoint joinPoint) {
    }

    /**
     * 执行顺序：3。
     * <br>
     * 当目标方法抛出异常时，执行 afterThrowing 方法。
     *
     * @param joinPoint 连接点
     * @param error     异常
     */
    @AfterThrowing(pointcut = "pointcut()", throwing = "error")
    public void afterThrowing(JoinPoint joinPoint, Throwable error) {
    }

    /**
     * 执行顺序：4。
     * <br>
     * 当目标方法成功/失败执行完毕时，都会执行 after 方法。
     *
     * @param joinPoint 连接点实例
     */
    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
    }

    /**
     * 执行顺序：5。
     * <br>
     * 当目标方法成功执行完毕时，执行 afterReturning 方法。
     *
     * @param joinPoint 连接点实例
     * @param returnVal 返回值
     */
    @AfterReturning(pointcut = "pointcut()", returning = "returnVal")
    public void afterReturning(JoinPoint joinPoint, Object returnVal) {
    }
}